const PENDING = "pending"; // 等待
const FULFILED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败

class MyPromise {
  constructor(executor) {
    try {
      // executor 执行器，会立即执行
      executor(this.resolve, this.reject);
    } catch (error) {
      // 捕获执行器抛出的异常
      this.reject(error);
    }
  }
  status = PENDING;
  value = undefined;
  reason = undefined;
  successCallback = []; //当出现异步时，存储then方法里的回调，在异步结束后再依次去执行数组里记录的回调函数。使用数组来存储then方法里的回调是因为then方法可以被多次的调用，
  failCallback = [];

  // 直接定义成箭头函数为了让this 指向MyPromise 这个实例
  resolve = (value) => {
    // 因为一旦状态确定就不可改变, 所以判断当前状态不是pending，直接return，阻止代码继续执行
    if (this.status !== PENDING) return;

    // 状态修改为 fulfilled
    this.status = FULFILED;

    // 保存成功的值(resolve和reject中保存值是为了后面then方法里的回掉函数需要成功或失败的值)
    this.value = value;

    // 当异步任务执行完后回去执行resolve方法，表示成功时，判断成功回调（这里的回调函数是then方法里的）是否存在，存在则去调用，同时将成功结果一并返回
    // this.successCallback && this.successCallback(value);
    while (this.successCallback.length) this.successCallback.shift()();
  };
  reject = (reason) => {
    // 因为一旦状态确定就不可改变, 所以判断当前状态不是pending，直接return，阻止代码继续执行
    if (this.status !== PENDING) return;

    // 状态修改为 rejected
    this.status = REJECTED;

    // 保存失败的原因
    this.reason = reason;

    // 当异步任务执行完后回去执行reject方法，表示失败时，判断失败回调是否存在，存在则去调用，同事将失败结果一并返回
    // this.failCallback && this.failCallback(reason);
    while (this.failCallback.length) this.failCallback.shift()();
  };

  //
  // 1. 链式调用，每个then方法都必须返回promise 对象
  // 2. 将上一个then 方法的返回值传递给下一个方法的回调函数
  then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      console.log(this.status);
      if (this.status === FULFILED) {
        setTimeout(() => {
          try {
            // 这里使用setTimeout变成异步 是为了获取promise2
            let x = successCallback(this.value); // 这里x 就是返回值
            // resolve(x); //这里调用resolve ，实际就是把x 传递给下一个then
            // -----------------------------
            // 判断x的值是普通值还是promise对象
            // 如果是普通值就直接调用resolve
            // 如果是promise对象，查看promise对象返回结果
            // 再根据promise对象返回结果决定调用resolve 还是reject
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            // 铺货then 回调方法里抛出的异常
            reject(error);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            // 这里使用setTimeout变成异步 是为了获取promise2
            let x = failCallback(this.reason); // 这里x 就是返回值
            // reject(x); //reject ，实际就是把x 传递给下一个then
            // -----------------------------
            // 判断x的值是普通值还是promise对象
            // 如果是普通值就直接调用resolve
            // 如果是promise对象，查看promise对象返回结果
            // 再根据promise对象返回结果决定调用resolve 还是reject
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            // 铺货then 回调方法里抛出的异常
            reject(error);
          }
        }, 0);
      } else {
        // 当实例的回调函数里是异步时，调用then方法时当前状态还是pending，所以在pending 时需要将then 方法的回调函数进行存储
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              // 这里使用setTimeout变成异步 是为了获取promise2
              let x = successCallback(this.value); // 这里x 就是返回值
              // resolve(x); //这里调用resolve ，实际就是把x 传递给下一个then
              // -----------------------------
              // 判断x的值是普通值还是promise对象
              // 如果是普通值就直接调用resolve
              // 如果是promise对象，查看promise对象返回结果
              // 再根据promise对象返回结果决定调用resolve 还是reject
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              // 铺货then 回调方法里抛出的异常
              reject(error);
            }
          }, 0);
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              // 这里使用setTimeout变成异步 是为了获取promise2
              let x = failCallback(this.reason); // 这里x 就是返回值
              // reject(x); //reject ，实际就是把x 传递给下一个then
              // -----------------------------
              // 判断x的值是普通值还是promise对象
              // 如果是普通值就直接调用resolve
              // 如果是promise对象，查看promise对象返回结果
              // 再根据promise对象返回结果决定调用resolve 还是reject
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              // 铺货then 回调方法里抛出的异常
              reject(error);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }

  // catch方法只需要成功回调undefined
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }

  // 无论成功还是失败最终都需要执行的方法，then方法实现
  finally(callback) {
    // finally 后可以继续调用then 需要返回
    return this.then(
      (value) => {
        // value 需要返回给下个then方法使用
        // 借助静态resolve 方法 处理异步情况
        return MyPromise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }

  static all(array) {
    if (!Array.isArray(array)) {
      // 非数组抛出类型异常
      return MyPromise.reject(new TypeError("args is not a array"));
    }

    let result = [];
    let index = 0; // 需要等待所有异步任务执行完毕才能result

    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }

      // 循环执行all 中传进来的数组
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        // 判断普通值还是promise对象
        if (current instanceof MyPromise) {
          // promise对象是执行当前值得then方法, 查看这个promised对象的返回结果，如果结果成功将值存放到result 中对应位置，如果失败执行reject
          current.then(
            (value) => addData(i, value),
            (reason) => reject(reason)
          );
        } else {
          // 普通值直接存放到result 中
          addData(i, array[i]);
        }
      }

      // resolve(result)
    });
  }

  static race(array) {
    if (!Array.isArray(array)) {
      // 非数组抛出类型异常
      return MyPromise.reject(new TypeError("args is not a array"));
    }

    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < array.length; i++) {
        MyPromise.resolve(array[i]).then(resolve, reject); // 只要又一个成功或失败，将结果返回
      }
    });
  }

  static resolve(value) {
    // 如果是promise 对象 直接返回
    if (value instanceof MyPromise) {
      return value;
    } else {
      // 如果不是, new个promise 对象 并返回
      return new MyPromise((resolve) => resolve(value));
    }
  }

  static reject(value) {
    return new MyPromise(undefined, (reject) => reject(value));
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 == x) {
    // 如果then方法返回的promise 与成功返回的promise相等 就报错不能相等
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>")
    );
  }

  if (x instanceof MyPromise) {
    // 如果是promise对象，查看promise对象返回结果,  再根据promise对象返回结果决定调用resolve 还是reject
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}

module.exports = MyPromise;
